/*
 * Alto2DEngine.cpp
 *
 *  Created on: 2009/02/05
 *      Author: Yongshik Cho
 */

#include "Alto2DEngine.h"
#include "xml/AltoSettings.h"
#include "TextureManager.h"
#include "Exception.h"
#include "event/MouseEvent.h"
#include "event/KeyEvent.h"
#include "event/TickEvent.h"
#include <locale.h>

#include "WorldManager.h"

const String Alto2DEngine::_DEFAULT_SCRIPT_FILE				= _T("main.nut");
const String Alto2DEngine::_DEFAULT_SCRIPT_FUNCTION			= _T("main");


Alto2DEngine::Alto2DEngine(HINSTANCE hInstance,
		HINSTANCE hPrevInstance,
		LPTSTR lpCmdLine,
		int32 nCmdShow, HWND hwnd)
{
	// TODO Auto-generated constructor stub

	//create LoggerableMananager
	mLoggerableManager = new LoggerableManager();
	if(!mLoggerableManager)
		AltoThrow(Exception::ERR_MEMORY);

	//read previous settings
	mSettings = AltoSettings::readSettings();

	if(!mSettings)
	{
		//there is no settings
		mRenderWindow = new RenderWindow(hInstance, hPrevInstance, lpCmdLine, nCmdShow, hwnd);
		if(!mRenderWindow)
			AltoThrow(Exception::ERR_MEMORY);
	}
	else
	{
		if(mSettings->logging)
		{
			Loggerable* log = new Loggerable();
			if(!log)
				AltoThrow(Exception::ERR_MEMORY);
		}

		mRenderWindow = new RenderWindow(hInstance, hPrevInstance, lpCmdLine, nCmdShow, hwnd,
				mSettings->title, Rect2D::CENTER_NONE, mSettings->x, mSettings->y,
				(int32)mSettings->width, (int32)mSettings->height,
				mSettings->fullscreen, mSettings->resizable);

		if(!mRenderWindow)
			AltoThrow(Exception::ERR_MEMORY);
	}

	mRenderSystem = new RenderSystem(mRenderWindow->getWnd(), mRenderWindow->getClientRect(), mSettings);
	if(!mRenderSystem)
		AltoThrow(Exception::ERR_MEMORY);

	mAudioSystem = new AudioSystem(mSettings);
	if(!mAudioSystem)
		AltoThrow(Exception::ERR_MEMORY);

	mResourceManager = new ResourceManager();
	if(!mResourceManager)
		AltoThrow(Exception::ERR_MEMORY);

	//initialize script manager
	ScriptManager& scriptManager = ScriptManager::GetInstance();
	scriptManager.bindScriptClasses();
	scriptManager.load(_DEFAULT_SCRIPT_FILE.c_str());
}

Alto2DEngine::~Alto2DEngine()
{
	// TODO Auto-generated destructor stub
	mSettings->x = mRenderWindow->getPosX();
	mSettings->y = mRenderWindow->getPosY();
	mSettings->width = mRenderWindow->getWidth();
	mSettings->height = mRenderWindow->getHeight();

	SAFE_DELETE(mResourceManager);
	SAFE_DELETE(mAudioSystem);
	SAFE_DELETE(mRenderSystem);
	SAFE_DELETE(mRenderWindow);

	//write current settings
	AltoSettings::writeSettings(mSettings);

	SAFE_DELETE(mLoggerableManager);
}

void Alto2DEngine::run()
{
	bool bQuit = false;
	MSG msg;

	//windows message pump loop
	while(!bQuit)
	{
		while(::PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
		{
			if(msg.message == WM_QUIT)
			{
				bQuit = true;
			}
			else
			{
				//retrieves scene manager
				SceneManager& sm = SceneManager::GetInstance();

				//retrieves root object
				RenderObjectContainer* root = sm.getRenderRoot();

				//retrieves current camera
				Camera* camera = sm.getActiveCamera();

				//translate to the origin of the root
				Rect2D rectRoot = root->getRect();
				Vector2D curCenter = rectRoot.getRuledRectPoint(root->getCenterType()); //0, 0
				Vector3D camPosition = camera->getPosition();
				Vector2D camScale = camera->getScale();
				Vector2D _camPosition = Vector2D(camPosition.x, camPosition.y);
				Vector2D trans = _camPosition * camScale - curCenter;

				POINT p;
				uint16 wFlags = (uint16)msg.wParam;
				p.x = (LONG)((float)(LOWORD(msg.lParam)));
				p.y = (LONG)((float)(HIWORD(msg.lParam)));
				p.x += (LONG)trans.x;
				p.y += (LONG)trans.y;

				if(msg.message == WM_LBUTTONDOWN)
				{
					MouseEvent e(MouseEvent::ON_LBUTTON_DOWN, root, wFlags, (uint16)p.x, (uint16)p.y);
					root->onMouseEvent(e);
				}

				if(msg.message == WM_LBUTTONUP)
				{
					MouseEvent e(MouseEvent::ON_LBUTTON_UP, root, wFlags, (uint16)p.x, (uint16)p.y);
					root->onMouseEvent(e);
				}

				if(msg.message == WM_MOUSEMOVE)
				{
					MouseEvent e(MouseEvent::ON_MOUSE_MOVE, root, wFlags, (uint16)p.x, (uint16)p.y);
					//root->onMouseEvent(e);
				}

				::TranslateMessage(&msg);
				::DispatchMessage(&msg);
			}
		}

		//do tick timer
		mRenderSystem->tick();

		//do render
		mRenderSystem->render();
	}
}

#ifdef _WIN32
int32 APIENTRY _tWinMain(HINSTANCE hInstance,
		HINSTANCE hPrevInstance,
		LPTSTR lpCmdLine,
		int32 nCmdShow)
{
	try
	{
#ifdef UNICODE
		setlocale(LC_ALL, "" );
#endif
		//create engine instance
		Alto2DEngine engine(hInstance, hPrevInstance, lpCmdLine, nCmdShow);

		//call script main function
		SquirrelFunction<void>((const SQChar*)(Alto2DEngine::_DEFAULT_SCRIPT_FUNCTION.c_str()))(lpCmdLine);

		World* world = WorldManager::GetInstance().getWorld();
		if(world)
		{
			RenderObjectContainer* renderRoot = SceneManager::GetInstance().getRenderRoot();
			if(renderRoot)
			{
				RenderObject* ro1 = renderRoot->getChildByName(_T("player1"));
				if(ro1)
					world->addBody(ro1);

				RenderObject* ro2 = renderRoot->getChildByName(_T("player2"));
				if(ro2)
					world->addBody(ro2);
			}
		}

		//run Alto2D engine
		engine.run();
	}
	catch(SquirrelError& e)
	{
		std::cout << _T("script exception occurred.") << std::endl;
		std::cout <<  _T(" exception : ") << e.desc << std::endl;
	}
	catch(const Exception& e)
	{
		std::cout << _T("Alto2DEngine exception occurred.") << std::endl;
		char mbstr[512]; memset(mbstr, 0, sizeof(mbstr));
#ifdef UNICODE
		::WideCharToMultiByte(CP_UTF8, 0, e.getDescription().c_str(), (int32)(e.getDescription().size()), mbstr, 512, 0, 0);
		std::cout << _T(" description : ") << mbstr << std::endl;
#else
		std::cout << _T(" description : ") << e.getDescription() << std::endl;
#endif
		std::cout << _T(" err code : ") << e.getCode() << std::endl;
#ifdef UNICODE
		::WideCharToMultiByte(CP_UTF8, 0, e.getFile().c_str(), (int32)(e.getFile().size()), mbstr, 512, 0, 0);
		std::cout << _T(" file : ") << mbstr << std::endl;
#else
		std::cout << _T(" file : ") << e.getFile() << std::endl;
#endif
		std::cout << _T(" line : ") << e.getLine() << std::endl;
	}
	catch(const std::exception& e)
	{
		std::cout << _T("standard exception : ");
		std::cout << e.what() << std::endl;
	}
	catch(...)
	{
		std::cout << _T("exception not catchable.") << std::endl;
	}

	return 0;
}
#else

#endif
